package com.aspose.words.maven.utils;

import java.io.File;
import java.io.IOException;
import java.net.URI;
import javax.swing.JOptionPane;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class AsposeMavenProjectManager {

	private File projectDir = null;

	/**
	 *
	 * @return
	 */
	public File getProjectDir() {
		return projectDir;
	}

	public String getDependencyVersionFromPOM(URI projectDir, String dependencyName) {
		try {
			String mavenPomXmlfile = projectDir.getPath() + File.separator + AsposeConstants.MAVEN_POM_XML;

			Document pomDocument = getXmlDocument(mavenPomXmlfile);

			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPath xpath = xPathfactory.newXPath();
			String expression = "//version[ancestor::dependency/artifactId[text()='" + dependencyName + "']]";
			XPathExpression xPathExpr = xpath.compile(expression);
			NodeList nl = (NodeList) xPathExpr.evaluate(pomDocument, XPathConstants.NODESET);

			if (nl != null && nl.getLength() > 0) {
				return nl.item(0).getTextContent();
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}
		return null;
	}

	private Document getXmlDocument(String mavenPomXmlfile)
			throws ParserConfigurationException, SAXException, IOException {
		DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
		DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
		Document pomDocument = (Document) docBuilder.parse(mavenPomXmlfile);

		return pomDocument;
	}

	public String getAsposeHomePath() {
		return System.getProperty("user.home") + File.separator + "aspose" + File.separator;
	}

	/**
	 *
	 * @param folderPath
	 */
	public static void checkAndCreateFolder(String folderPath) {
		File folder = new File(folderPath);
		if (!folder.exists()) {
			folder.mkdirs();
		}
	}

	// Singleton instance
	private static AsposeMavenProjectManager asposeMavenProjectManager = new AsposeMavenProjectManager();

	/**
	 *
	 * @return
	 */
	public static AsposeMavenProjectManager getInstance() {
		return asposeMavenProjectManager;
	}

	/**
	 *
	 * @param mavenPomXmlfile
	 * @param excludeGroup
	 * @return
	 */
	public NodeList getDependenciesFromPOM(String mavenPomXmlfile, String excludeGroup) {
		try {

			Document pomDocument = getXmlDocument(mavenPomXmlfile);

			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPath xpath = xPathfactory.newXPath();
			String expression = "//dependency[child::groupId[text()!='" + excludeGroup + "']]";
			XPathExpression xPathExpr = xpath.compile(expression);
			NodeList nl = (NodeList) xPathExpr.evaluate(pomDocument, XPathConstants.NODESET);
			if (nl != null && nl.getLength() > 0) {
				return nl;
			}
		} catch (IOException | ParserConfigurationException | SAXException | XPathExpressionException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 *
	 * @param addTheseDependencies
	 */
	public void addMavenDependenciesInProject(NodeList addTheseDependencies) {

		String mavenPomXmlfile = projectDir.getPath() + File.separator + AsposeConstants.MAVEN_POM_XML;

		try {
			Document pomDocument = getXmlDocument(mavenPomXmlfile);

			Node dependenciesNode = pomDocument.getElementsByTagName("dependencies").item(0);

			if (addTheseDependencies != null && addTheseDependencies.getLength() > 0) {
				for (int n = 0; n < addTheseDependencies.getLength(); n++) {
					String artifactId = addTheseDependencies.item(n).getFirstChild().getNextSibling().getNextSibling()
							.getNextSibling().getFirstChild().getNodeValue();

					XPathFactory xPathfactory = XPathFactory.newInstance();
					XPath xpath = xPathfactory.newXPath();
					String expression = "//artifactId[text()='" + artifactId + "']";

					XPathExpression xPathExpr = xpath.compile(expression);

					Node dependencyAlreadyExist = (Node) xPathExpr.evaluate(pomDocument, XPathConstants.NODE);

					if (dependencyAlreadyExist != null) {
						Node dependencies = pomDocument.getElementsByTagName("dependencies").item(0);
						dependencies.removeChild(dependencyAlreadyExist.getParentNode());
					}

					Node importedNode = pomDocument.importNode(addTheseDependencies.item(n), true);
					dependenciesNode.appendChild(importedNode);

				}
			}
			removeEmptyLinesfromDOM(pomDocument);
			writeToPOM(pomDocument);

		} catch (ParserConfigurationException | SAXException | XPathExpressionException | IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 *
	 * @param mavenPomXmlfile
	 * @param excludeURL
	 * @return
	 */
	public NodeList getRepositoriesFromPOM(String mavenPomXmlfile, String excludeURL) {

		try {

			Document pomDocument = getXmlDocument(mavenPomXmlfile);

			XPathFactory xPathfactory = XPathFactory.newInstance();
			XPath xpath = xPathfactory.newXPath();
			String expression = "//repository[child::url[not(starts-with(.,'" + excludeURL + "'))]]";
			XPathExpression xPathExpr = xpath.compile(expression);
			NodeList nl = (NodeList) xPathExpr.evaluate(pomDocument, XPathConstants.NODESET);
			if (nl != null && nl.getLength() > 0) {
				return nl;
			}
		} catch (IOException | ParserConfigurationException | SAXException | XPathExpressionException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 *
	 * @param addTheseRepositories
	 */
	public void addMavenRepositoriesInProject(NodeList addTheseRepositories) {
		String mavenPomXmlfile = projectDir.getPath() + File.separator + AsposeConstants.MAVEN_POM_XML;

		try {
			Document pomDocument = getXmlDocument(mavenPomXmlfile);

			Node repositoriesNode = pomDocument.getElementsByTagName("repositories").item(0);

			if (addTheseRepositories != null && addTheseRepositories.getLength() > 0) {
				for (int n = 0; n < addTheseRepositories.getLength(); n++) {
					String repositoryId = addTheseRepositories.item(n).getFirstChild().getNextSibling().getFirstChild()
							.getNodeValue();

					XPathFactory xPathfactory = XPathFactory.newInstance();
					XPath xpath = xPathfactory.newXPath();
					String expression = "//id[text()='" + repositoryId + "']";

					XPathExpression xPathExpr = xpath.compile(expression);

					Boolean repositoryAlreadyExist = (Boolean) xPathExpr.evaluate(pomDocument, XPathConstants.BOOLEAN);

					if (!repositoryAlreadyExist) {
						Node importedNode = pomDocument.importNode(addTheseRepositories.item(n), true);
						repositoriesNode.appendChild(importedNode);
					}

				}
			}
			removeEmptyLinesfromDOM(pomDocument);
			writeToPOM(pomDocument);

		} catch (XPathExpressionException | SAXException | ParserConfigurationException | IOException ex) {
			ex.printStackTrace();
		}
	}

	/**
	 *
	 * @param pomDocument
	 * @throws IOException
	 */
	public void writeToPOM(Document pomDocument) throws IOException {
		try {
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			DOMSource source = new DOMSource(pomDocument);
			StreamResult result = new StreamResult(
					new File(projectDir + File.separator + AsposeConstants.MAVEN_POM_XML));
			transformer.setOutputProperty(OutputKeys.INDENT, "yes");
			transformer.transform(source, result);
		} catch (TransformerException e) {
			e.printStackTrace();
		}
	}

	private void removeEmptyLinesfromDOM(Document doc) throws XPathExpressionException {
		XPath xp = XPathFactory.newInstance().newXPath();
		NodeList nl = (NodeList) xp.evaluate("//text()[normalize-space(.)='']", doc, XPathConstants.NODESET);

		for (int i = 0; i < nl.getLength(); ++i) {
			Node node = nl.item(i);
			node.getParentNode().removeChild(node);
		}
	}

	/**
	 *
	 * @param title
	 * @param message
	 * @param buttons
	 * @param icon
	 * @return
	 */
	public static int showMessage(String title, String message, int buttons, int icon) {
		int result = JOptionPane.showConfirmDialog(null, message, title, buttons, icon);
		return result;
	}

	public static AsposeMavenProjectManager initialize(File prjDir) {
		asposeMavenProjectManager = new AsposeMavenProjectManager();
		asposeMavenProjectManager.projectDir = prjDir;
		return asposeMavenProjectManager;
	}

}